import annotations.AnnotationHelper
import annotations.QueryObject
import annotations.Queryable
import org.springframework.beans.factory.InitializingBean

/**
 * service to convert from an entry to an entry
 */

class ConvertService implements InitializingBean {

  boolean transactional = true

  def grailsApplication
  def setting

  void afterPropertiesSet() { this.setting = grailsApplication.config.setting }

  LookupService lookupService

  /**
   * index over the required keys
   */
  Map<String, String> methodFromNames = new HashMap<String, String>()

  /**
   * index over the required keys
   */
  Map<String, String> methodToNames = new HashMap<String, String>()

  /**
   * constructor which initializes the mapping of compounds
   */
  public ConvertService() {

    //contains all possible froms
    Collection<QueryObject> fromSet = AnnotationHelper.getQueryableAnnotations(LookupService.class)

    //contains all possible tos
    Collection<QueryObject> toSet = AnnotationHelper.getQueryableAnnotations(ConvertService.class)

    //generate our lookup association
    for (QueryObject q: fromSet) {
      methodFromNames.put(q.annotation.name().toLowerCase(), q.getMethod().getName())
    }

    //generate our lookup association
    for (QueryObject q: toSet) {
      methodToNames.put(q.annotation.name().toLowerCase(), q.getMethod().getName())
    }
  }
  /**
   * from what can we convert
   */
  Collection<String> getFromConversions() {
    return methodFromNames.keySet().sort()
  }

  /**
   * get to conversions
   */
  Collection<String> getToConversions() {
    return methodToNames.keySet().sort()
  }

  /**
   * does the actual converting
   */
  Collection<Map<Compound, ?>> convert(String from, def to, def value) {

    //convert the names to lowercase
    from = from.toLowerCase()
    //to = to.toLowerCase()  commented by pradeep

    //get the lookup name
    String fromMethod = methodFromNames[from]
    //String toMethod = methodToNames[to]     commented by pradeep

    //make sure we actually know the mapping
    if (fromMethod == null) {
      return ["error, unknown from mapping, ${from}"]
    }

//    if (toMethod == null) {
//      return ["error, unknown to mapping, ${to}"]
//    }

    //fetches the compounds
    def compounds = lookupService."$fromMethod"(value)

    log.debug("found: ${compounds}")

    //contains our results
    Collection<Map<Compound, ?>> result = new Vector<Map<Compound, ?>>()

    //make sure we have a list or a compound
    if (compounds instanceof Collection) {

      //query all the compounds in the list
      compounds.each {Compound compound ->
        filterResult(compound, result, to)
      }
    }
    //query a single compound
    else if (compounds instanceof Compound)
    {
      filterResult(compounds, result, toMethod)
    }
    else {
      return ["error, we exspected to receive a compound or a list of compounds from the method ${fromMethod}"]
    }

    log.debug("return: ${result}")
    //return our result
    return result
  }

  /**
   * filter the result and omit empty collections
   */
  private def filterResult(Compound compound, Collection<Map<Compound, ?>> result, def toMethod)
  {
    //log.debug "toMethod ::: ${toMethod.getClass()}"
    if( toMethod instanceof Collection )
    {
      HashMap temp1 = new HashMap()
      for(String s:toMethod)
      {
        String toMethod1 = methodToNames[s]
        if (toMethod1 == null) {
          return ["error, unknown to mapping, ${s}"]
        }
        def temp = this."${toMethod1}"(compound)
        temp1.put(s.trim(),temp);
      }

      if (temp1 instanceof Collection) {
        if (!temp1.isEmpty()) {
          result.add([compound: compound, result: temp1])
        }
      }
      else {
        result.add([compound: compound, result: temp1])
      }
    } else
    if( toMethod instanceof String )
    {
      String toMethod1 = methodToNames[toMethod]
      if (toMethod1 == null)
      {
          return ["error, unknown to mapping, ${toMethod}"]
        }
      def temp = this."${toMethod1}"(compound)

      if (temp instanceof Collection) {
        if (!temp.isEmpty()) {
          result.add([compound: compound, result: temp])
        }
      }
      else {
        result.add([compound: compound, result: temp])
      }
    }   
  }

  /**
   * returns the compound or list of compounds
   */
  @Queryable(name = "compound")
  def convertToCOMPOUND(Compound compound) {
    return compound
  }

  /**
   * converts to inchi key
   */
  @Queryable(name = "inchi")
  def convertToINCHI(Compound compound) {
    return compound.inchi
  }

  /**
   * converts to inchi hash key
   */
  @Queryable(name = "inchikey")
  def convertToINCHIKEY(Compound compound) {
    return compound.inchiHashKey
  }

  /**
   * converts to db links hash key
   */
  @Queryable(name = "links")
  def convertToLINKS(Compound compound) {
    return compound.dbLinks
  }

  /**
   * converts to smile
   */
  @Queryable(name = "smiles")
  def convertToSMILES(Compound compound) {
    return compound.smiles
  }

  /**
   * converts to iupac name
   */
  @Queryable(name = "iupac")
  def convertToIUPAC(Compound compound) {
    return compound.iupac
  }

  /**
   * converts to synonyms
   */
  @Queryable(name = "names")
  def convertToNAMES(Compound compound) {
    return compound.synonyms
  }

  /**
   * converts to molare mass
   */
  @Queryable(name = "mass")
  def convertToMASS(Compound compound) {
    return compound.exactMolareMass
  }

  /**
   * converts to molare mass
   */
  @Queryable(name = "formula")
  def convertToFORMULA(Compound compound) {
    if (compound.formula != null)
      return compound.formula
    else {
      return "unknown"
    }
  }

  /**
   * converts to cas
   */
  @Queryable(name = "cas")
  def convertToCas(Compound compound) {
    return Cas.findAllByCompound(compound)
  }

  /**
   * converts to kegg
   */
  @Queryable(name = "kegg")
  def convertToKegg(Compound compound) {
    return Kegg.findAllByCompound(compound)
  }

  /**
   * converts to lipidmap
   */
  @Queryable(name = "lipidmap")
  def convertToLipidMap(Compound compound) {
    return LipidMap.findAllByCompound(compound)
  }

  /**
   * converts to cid
   */
  @Queryable(name = "cid")
  def convertToCID(Compound compound) {
    return PubchemCompound.findAllByCompound(compound)
  }

  /**
   * converts to sid
   */
  @Queryable(name = "sid")
  def convertToSID(Compound compound) {
    return PubchemSubstance.findAllByCompound(compound)
  }

  /**
   * converts to hmdb
   */
  @Queryable(name = "hmdb")
  def convertToHMDB(Compound compound) {
    return HMDB.findAllByCompound(compound)
  }

  /**
   * converts to chebi
   */
  @Queryable(name = "chebi")
  def convertToChebi(Compound compound) {
    return Chebi.findAllByCompound(compound)
  }

}
